package com.retail.lottery.colalottery.utils;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import com.retail.lottery.colalottery.constant.SysConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Order(2)
public class RedisUtils {
    private static StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    @Resource
    private RedisTemplate<String, String> stringRedisTemplate;
    @Resource
    private RedisTemplate<String, Float> floatRedisTemplate;
    @Resource
    private RedisTemplate<String, Long> longRedisTemplate;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 修改为可以静态访问
     */
    private static RedisUtils cacheUtils;
    /**
     * 加锁的lua脚本
     */
    public static final DefaultRedisScript<String> LOCK_LUA = new DefaultRedisScript();
    /**
     * 解锁的lua脚本
     */
    public static final DefaultRedisScript<String> UNLOCK_LUA = new DefaultRedisScript();

    static {
        LOCK_LUA.setResultType(String.class);
        LOCK_LUA.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/getLock.lua")));
        UNLOCK_LUA.setResultType(String.class);
        UNLOCK_LUA.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/releaseLock.lua")));
    }

    @PostConstruct
    void init() {
        cacheUtils = this;
        cacheUtils.stringRedisTemplate = this.stringRedisTemplate;
        cacheUtils.floatRedisTemplate = this.floatRedisTemplate;
        cacheUtils.longRedisTemplate = this.longRedisTemplate;
    }

    /**
     * 将数据存入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public static void saveString(String key, String value) {
        ValueOperations<String, String> vo = cacheUtils.stringRedisTemplate.opsForValue();
        vo.set(key, value);
    }

//    /**
//     * 将数据存入缓存（并设置失效时间+随机数）
//     *
//     * @param key
//     * @param value
//     * @param seconds
//     * @return
//     */
//    public static void saveRandomString(String key, String value, long seconds, TimeUnit unit) {
//        // 转换为毫秒
//        long millis = unit.toMillis(seconds);
//        cacheUtils.stringRedisTemplate.opsForValue().set(key, value, RedisUtils.random(millis), TimeUnit.MILLISECONDS);
//    }

    /**
     * 执行
     */
    public static Long execute(RedisScript<Long> redisScript, List<String> keys, String... args) {
        return (Long) cacheUtils.stringRedisTemplate.execute(redisScript, keys, args);
    }

    /**
     * 将数据存入缓存（并设置失效时间）
     *
     * @param key
     * @param value
     * @param seconds
     * @return
     */
    public static void saveString(String key, String value, long seconds, TimeUnit unit) {
        cacheUtils.stringRedisTemplate.opsForValue().set(key, value, seconds, unit);
    }

    /**
     * 从缓存中取得字符串数据
     *
     * @param key
     * @return 数据
     */
    public static String getString(String key) {
        return cacheUtils.stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 将数据存入缓存的集合中
     *
     * @param key
     * @param value
     * @return
     */
    public static void saveToSet(String key, String value) {
        SetOperations<String, String> so = cacheUtils.stringRedisTemplate.opsForSet();
        so.add(key, value);
    }

    /**
     * @param key 缓存Key
     * @return keyValue
     * @author:mijp
     * @since:2017/1/16 13:23
     */
    public static String getFromSet(String key) {
        return cacheUtils.stringRedisTemplate.opsForSet().pop(key);
    }

    /**
     * 将 key的值保存为 value ，当且仅当 key 不存在。 若给定的 key 已经存在，则 SETNX 不做任何动作。 SETNX 是『SET if Not eXists』(如果不存在，则 SET)的简写。 <br>
     * 保存成功，返回 true <br>
     * 保存失败，返回 false
     */
    public static boolean saveNX(String key, String value) {
        /** 设置成功，返回 1 设置失败，返回 0 **/
        return cacheUtils.stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.setNX(key.getBytes(), value.getBytes());
        });

    }

    /**
     * 将 key的值保存为 value ，当且仅当 key 不存在。 若给定的 key 已经存在，则 SETNX 不做任何动作。 SETNX 是『SET if Not eXists』(如果不存在，则 SET)的简写。 <br>
     * 保存成功，返回 true <br>
     * 保存失败，返回 false
     *
     * @param key
     * @param value
     * @param expire 超时时间
     * @return 保存成功，返回 true 否则返回 false
     */
    public static boolean saveNX(String key, String value, long expire, TimeUnit timeUnit) {
        boolean ret = saveNX(key, value);
        if (ret) {
            cacheUtils.stringRedisTemplate.expire(key, expire, timeUnit);
        }
        return ret;
    }

    /**
     * 将自增变量存入缓存
     */
    public static void saveLongSequence(String key, long seqNo) {
        cacheUtils.longRedisTemplate.delete(key);
        cacheUtils.longRedisTemplate.opsForValue().increment(key, seqNo);
    }

    /**
     * 将递增浮点数存入缓存
     */
    public static void saveFloatSequence(String key, float data) {
        cacheUtils.floatRedisTemplate.delete(key);
        cacheUtils.floatRedisTemplate.opsForValue().increment(key, data);
    }

    /**
     * 保存复杂类型数据到缓存
     *
     * @param key
     * @param obj
     * @return
     */
    public static void saveBean(String key, Object obj) {
        cacheUtils.stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(obj));
    }

    /**
     * 保存复杂类型数据到缓存（并设置失效时间）
     *
     * @param key
     * @param obj
     * @param seconds
     * @return
     */
    public static void saveBean(String key, Object obj, long seconds) {
        cacheUtils.stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(obj), seconds, TimeUnit.SECONDS);
    }

    /**
     * 功能: 存到指定的队列中<br />
     * 左近右出<br\> 作者: 耿建委
     *
     * @param key
     * @param value
     * @param size  队列大小限制 0：不限制
     */
    public static void saveToQueue(String key, String value, long size) {
        ListOperations<String, String> lo = cacheUtils.stringRedisTemplate.opsForList();

        if (size > 0 && lo.size(key) >= size) {
            lo.rightPop(key);
        }
        lo.leftPush(key, value);
    }

    /**
     * 保存到hash集合中
     *
     * @param hName 集合名
     * @param key
     * @param value
     */
    public static void hashSet(String hName, String key, String value) {
        cacheUtils.stringRedisTemplate.opsForHash().put(hName, key, value);
    }

    /**
     * 根据key获取所以值
     *
     * @param key
     * @return
     */
    public static Map<String, String> hgetAll(String key) {
        HashOperations<String, String, String> operations = cacheUtils.stringRedisTemplate.opsForHash();
        return operations.entries(key);
    }

    /**
     * 保存到hash集合中
     *
     * @param <T>
     * @param hName 集合名
     * @param key
     * @param t
     */
    public static <T> void hashSet(String hName, String key, T t) {
        hashSet(hName, key, JSON.toJSONString(t));
    }

    /**
     * 取得复杂类型数据
     *
     * @param key
     * @param clazz
     * @return
     */
    public static <T> T getBean(String key, Class<T> clazz) {
        String value = cacheUtils.stringRedisTemplate.opsForValue().get(key);
        if (value == null) {
            return null;
        }

        JSONObject obj = JSONObject.parseObject(value);
        return JSONObject.toJavaObject(obj, clazz);
        // JSON.toJSONString(obj, SerializerFeature.WriteNonStringValueAsString)
        // return JSON.parseObject(obj.toJSONString(), clazz);
    }

    /**
     * 功能: 从指定队列里取得数据<br />
     * 作者: 耿建委
     *
     * @param key
     * @param size 数据长度
     * @return
     */
    public static List<String> getFromQueue(String key, long size) {
        boolean flag = cacheUtils.stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.exists(key.getBytes());
        });

        if (flag) {
            return new ArrayList<>();
        }
        ListOperations<String, String> lo = cacheUtils.stringRedisTemplate.opsForList();
        if (size > 0) {
            return lo.range(key, 0, size - 1);
        } else {
            return lo.range(key, 0, lo.size(key) - 1);
        }
    }

    /**
     * 功能: 从指定队列里取得数据<br />
     * 作者: 耿建委
     *
     * @param key
     * @return
     */
    public static String popQueue(String key) {
        return cacheUtils.stringRedisTemplate.opsForList().rightPop(key);
    }

    /**
     * 取得序列值的下一个
     *
     * @param key
     * @return
     */
    public static Long getLongSequenceNext(String key) {
        return cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.incr(key.getBytes());
        });
    }

    /**
     * 取得序列值的下一个
     *
     * @param key
     * @return
     */
    public static Long getLongSequenceNext(String key, long value) {
        return cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.incrBy(key.getBytes(), value);
        });
    }

    /**
     * 将序列值回退一个
     *
     * @param key
     * @return
     */
    public static void getLongSequenceBack(String key) {
        cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> connection.decr(key.getBytes()));
    }

    /**
     * 从hash集合里取得
     *
     * @param hName
     * @param key
     * @return
     */
    public static String hashGet(String hName, String key) {
        HashOperations<String, String, String> operations = cacheUtils.redisTemplate.opsForHash();
        return operations.get(hName, key);
    }
    public static Boolean isHashExist(String hName) {
        return cacheUtils.redisTemplate.hasKey(hName);
    }
    public static <T> T hashGet(String hName, String key, Class<T> clazz) {
        String result = hashGet(hName, key);
        return JSON.parseObject(result, clazz);
    }

    /**
     * 增加浮点数的值
     *
     * @param key
     * @return
     */
    public static Long getFloatSequenceNext(String key, int incrBy) {
        return cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.incrBy(key.getBytes(), incrBy);
        });
    }

    /**
     * 判断是否缓存了数据
     *
     * @param key 数据KEY
     * @return 判断是否缓存了
     */
    public static boolean isCached(String key) {
        return cacheUtils.stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.exists(key.getBytes());
        });
    }

    /**
     * 判断是否缓存在指定的集合中
     *
     * @param key   数据KEY
     * @param value 数据
     * @return 判断是否缓存了
     */
    public static boolean isMember(String key, String value) {
        return cacheUtils.stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.sIsMember(key.getBytes(), value.getBytes());
        });
    }

    /**
     * 从缓存中删除数据
     *
     * @param key
     * @return
     */
    public static void delKey(String key) {
        cacheUtils.stringRedisTemplate.execute((RedisCallback<Long>) connection -> connection.del(key.getBytes()));
    }
    /**
     * 从缓存中删除数据
     *
     * @param key
     * @return
     */
    public static void delOriginKey(String key) {
        cacheUtils.redisTemplate.execute((RedisCallback<Long>) connection -> connection.del(key.getBytes()));
    }

    /**
     * 设置超时时间
     *
     * @param key
     * @param seconds
     */
    public static void expire(String key, int seconds) {
        cacheUtils.stringRedisTemplate
                .execute((RedisCallback<Boolean>) connection -> connection.expire(key.getBytes(), seconds));

    }

    /**
     * 列出set中所有成员
     *
     * @param setName set名
     * @return
     */
    public static Set<String> getSet(String setName) {
        HashOperations<String, String, String> operations = cacheUtils.stringRedisTemplate.opsForHash();
        return operations.keys(setName);
    }

    /**
     * 以map集合的形式添加键值对
     *
     * @param key
     * @param maps
     */
    public static void hashPutAll(String key, Map<String, String> maps, long expire, TimeUnit timeUnit) {
        cacheUtils.redisTemplate.opsForHash().putAll(key, maps);
        cacheUtils.redisTemplate.expire(key, expire, timeUnit);
    }

    /**
     * 向set中追加一个值
     *
     * @param setName set名
     * @param value
     */
    public static void saveSet(String setName, String value) {
        cacheUtils.stringRedisTemplate
                .execute((RedisCallback<Long>) connection -> connection.sAdd(setName.getBytes(), value.getBytes()));
    }

    /**
     * 逆序列出sorted set包括分数的set列表
     *
     * @param key   set名
     * @param start 开始位置
     * @param end   结束位置
     * @return 列表
     */
    public static Set<RedisZSetCommands.Tuple> getSortedSetWithScores(String key, int start, int end) {
        return cacheUtils.stringRedisTemplate.execute((RedisCallback<Set<RedisZSetCommands.Tuple>>) connection -> {
            return connection.zRevRangeWithScores(key.getBytes(), start, end);
        });
    }

    /**
     * 逆序取得sorted sort排名
     *
     * @param key    set名
     * @param member 成员名
     * @return 排名
     */
    public static Long getRankOfSortedSet(String key, String member) {
        return cacheUtils.stringRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.zRevRank(key.getBytes(), member.getBytes());
        });
    }

    /**
     * 向sorted set中追加一个值
     *
     * @param key    set名
     * @param score  分数
     * @param member 成员名称
     */
    public static void saveToSortedSet(String key, Integer score, String member) {
        cacheUtils.stringRedisTemplate.execute(
                (RedisCallback<Boolean>) connection -> connection.zAdd(key.getBytes(), score, member.getBytes()));
    }

    /**
     * 从sorted set删除一个值
     *
     * @param key    set名
     * @param member 成员名称
     */
    public static void deleteFromSortedSet(String key, String member) {
        cacheUtils.stringRedisTemplate
                .execute((RedisCallback<Long>) connection -> connection.zRem(key.getBytes(), member.getBytes()));
    }

    /**
     * 判断hash集合中是否缓存了数据
     *
     * @param hName
     * @param key   数据KEY
     * @return 判断是否缓存了
     */
    public static boolean hashCached(String hName, String key) {
        return cacheUtils.stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.hExists(key.getBytes(), key.getBytes());
        });
    }

    /**
     * 从hash map中取得复杂类型数据
     *
     * @param key
     * @param field
     * @param clazz
     */
    public static <T> T getFromHash(String key, String field, Class<T> clazz) {
        byte[] input = cacheUtils.stringRedisTemplate.execute((RedisCallback<byte[]>) connection -> {
            return connection.hGet(key.getBytes(), field.getBytes());
        });
        return JSON.parseObject(input, clazz, Feature.AutoCloseSource);
    }

    /**
     * 从hashmap中删除一个值
     *
     * @param key   map名
     * @param field 成员名称
     */
    public static void deleteFromHash(String key, String field) {
        cacheUtils.stringRedisTemplate
                .execute((RedisCallback<Long>) connection -> connection.hDel(key.getBytes(), field.getBytes()));
    }

    /**
     * 保存到hash集合中 只在 key 指定的哈希集中不存在指定的字段时，设置字段的值。如果 key 指定的哈希集不存在，会创建一个新的哈希集并与 key 关联。如果字段已存在，该操作无效果。
     *
     * @param hashName 集合名
     * @param field
     * @param value
     */
    public static void setHashFieldValueNX(final String hashName, final String field, final String value) {
        cacheUtils.stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> connection
                .hSetNX(hashName.getBytes(), field.getBytes(), value.getBytes()));
    }

    /**
     * 保存到hash集合中 只在 key 指定的哈希集中不存在指定的字段时，设置字段的值。如果 key 指定的哈希集不存在，会创建一个新的哈希集并与 key 关联。如果字段已存在，该操作无效果。
     *
     * @param <T>
     * @param hashName 集合名
     * @param field
     * @param t
     */
    public static <T> void setHashFieldValueNX(String hashName, String field, T t) {
        setHashFieldValueNX(hashName, field, JSON.toJSONString(t));
    }

    /**
     * 将所有指定的值插入到存于 key 的列表的头部。如果 key 不存在，那么在进行 push 操作前会创建一个空列表
     *
     * @param <T>
     * @param key
     * @param value
     * @return
     */
    public static <T> Long lpush(String key, T value) {
        return value instanceof String
                ? cacheUtils.stringRedisTemplate.opsForList().leftPush(key, String.valueOf(value))
                : cacheUtils.stringRedisTemplate.opsForList().leftPush(key, JSON.toJSONString(value));
    }

    /**
     * 只有当 key 已经存在并且存着一个 list 的时候，在这个 key 下面的 list 的头部插入 value。 与 LPUSH 相反，当 key 不存在的时候不会进行任何操作
     *
     * @param key
     * @param value
     * @return
     */
    public static <T> Long lpushx(String key, T value) {
        return cacheUtils.stringRedisTemplate.opsForList().leftPushIfPresent(key, JSON.toJSONString(value));
    }

    /**
     * 返回存储在 key 里的list的长度。 如果 key 不存在，那么就被看作是空list，并且返回长度为 0
     *
     * @param key
     * @return
     */
    public static Long llen(String key) {
        return cacheUtils.stringRedisTemplate.opsForList().size(key);
    }

    /**
     * 返回存储在 key 的列表里指定范围内的元素。 start 和 end 偏移量都是基于0的下标，即list的第一个元素下标是0（list的表头），第二个元素下标是1，以此类推
     *
     * @param key
     * @return
     */
    public static List<String> lrange(String key, long start, long end) {
        return cacheUtils.stringRedisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 移除并且返回 key 对应的 list 的第一个元素
     *
     * @param key
     * @return
     */
    public static String lpop(String key) {
        return cacheUtils.stringRedisTemplate.opsForList().leftPop(key);
    }

    /**
     * 从队列的右侧移除并且返回 key 对应的 list 的第一个元素
     *
     * @param key
     * @return
     */
    public static String rpop(String key) {
        return cacheUtils.stringRedisTemplate.opsForList().rightPop(key);
    }

    /**
     * key 自减
     *
     * @param key
     * @return
     */
    public static Long decr(String key) {
        return cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.decr(key.getBytes());
        });
    }

    /**
     * key 自增
     *
     * @param key
     * @return
     */
    public static Long incr(String key) {
        return cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.incr(key.getBytes());
        });
    }

    /**
     * key 自减
     *
     * @param key
     * @param count 自减数量
     * @return
     */
    public static Long decrBy(String key, long count) {
        return cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.decrBy(key.getBytes(), count);
        });
    }

    /**
     * key 自增
     *
     * @param key
     * @param count 自增数量
     * @return
     */
    public static Long incrBy(String key, long count) {
        return cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.incrBy(key.getBytes(), count);
        });
    }

    /**
     * key 自增
     *
     * @param key
     * @param count
     * @return
     */
    public static Long increment(String key, long count) {
        return cacheUtils.floatRedisTemplate.opsForValue().increment(key, count);
    }

    /**
     * 模糊查询redis keys
     *
     * @param key 模糊键
     * @return
     */
    public static Set<String> keys(String key) {
        return cacheUtils.stringRedisTemplate.keys(key);
    }

    /**
     * 通过key获得值
     *
     * @param key
     * @return
     */
    public static String get(String key) {
        byte[] result = cacheUtils.longRedisTemplate.execute((RedisCallback<byte[]>) connection -> {
            return connection.get(key.getBytes());
        });

        if (null != result) {
            return new String(result);
        } else {
            return null;
        }
    }

    /**
     * 删除key
     *
     * @param key
     * @return
     */
    public static Long del(String key) {
        return cacheUtils.longRedisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.del(key.getBytes());
        });
    }

    /**
     * 清空list,start >end 清空部分 start,end
     *
     * @param key
     * @return
     */
    public static void ltrim(String key, long start, long end) {
        cacheUtils.stringRedisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * 批量push list
     *
     * @param key
     * @return
     */
    public static Long leftPushAll(String key, List<String> value) {
        return cacheUtils.stringRedisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 通过管道批量处理list获得数据
     *
     * @param key
     * @param number
     * @return
     */
    public static List<String> getStringList4Pipeline(String key, int number) {

        RedisCallback<List<Object>> callback = new RedisCallback<List<Object>>() {
            @Override
            public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
                connection.openPipeline();
                // 取出开始-结束的数据
                connection.lRange(key.getBytes(), SysConstant.ZERO_BYTE, number - 1);
                // 保留这个endindex+1 到最后一个数据
                connection.lTrim(key.getBytes(), number, -1);
                return connection.closePipeline();
            }
        };

        List<Object> redisResult = cacheUtils.stringRedisTemplate.execute(callback);

        List<String> result = redisResult.stream().flatMap(o -> ((ArrayList<byte[]>) o).stream()).map(String::new)
                .collect(Collectors.toList());

        return result;
    }

    /**
     * 判断键key是否存在
     *
     * @param key
     * @return
     */
    public static boolean exists(String key) {

        return cacheUtils.longRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.exists(key.getBytes());
        });
    }

    /**
     * 加锁操作
     *
     * @param key        Redis 锁的 key 值
     * @param requestId  请求id，防止解了不该由自己解的锁 (随机生成)
     * @param expireTime 锁的超时时间(毫秒)
     * @return true or false
     */
    @SuppressWarnings("unchecked")
    public static boolean lockWithLua(String key, String requestId, Long expireTime) {
        try {
            Object result = cacheUtils.stringRedisTemplate.execute(LOCK_LUA, stringRedisSerializer
                    , stringRedisSerializer, Collections.singletonList(key), requestId, String.valueOf(expireTime));
            if (SysConstant.REDIS_EXEC_RESULT_SUCCESS.equals(String.valueOf(result))) {
                return true;
            }
        } catch (Exception e) {
            log.error("lockWithLua-exception", e);
        }
        return false;
    }

    /**
     * 解锁操作
     *
     * @param key       Redis 锁的 key 值
     * @param requestId 请求 id, 防止解了不该由自己解的锁 (随机生成)
     * @return true or false
     */
    @SuppressWarnings("unchecked")
    public static boolean unLockWithLua(String key, String requestId) {
        try {
            String result = cacheUtils.stringRedisTemplate.execute(UNLOCK_LUA, stringRedisSerializer
                    , stringRedisSerializer, Collections.singletonList(key), requestId);
            if (SysConstant.REDIS_EXEC_RESULT_SUCCESS.equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error("unLockWithLua-exception", e);
        }
        return false;
    }


//    public static Long random(Long seconds) {
//        return seconds + RandomUtils.getRandom(1, 1000);
//    }

    public RedisTemplate getRedisTemplate() {
        return this.stringRedisTemplate;
    }

    public static void set(String key, String value) {
        cacheUtils.stringRedisTemplate.opsForValue().set(key, value);
    }

    public static Long getStringExpire(String key) {
        return cacheUtils.stringRedisTemplate.opsForValue().getOperations().getExpire(key);
    }

}
